package com.hongru.system.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.hongru.api.SysLogApi;
import com.hongru.aspect.annotation.Log;
import com.hongru.ebean.EbeanUtil;
import com.hongru.system.entity.SysLog;
import com.hongru.util.BeanChangeUtil;
import com.hongru.util.IpUtils;
import com.hongru.vo.LoginUser;
import io.ebean.DB;
import io.swagger.annotations.ApiModelProperty;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @Description
 * @Copyright (c) 1998-2022 北京新鸿儒世纪网络技术有限公司 All Rights Reserved.
 * @Url https://www.xinhongru.com
 * @ClassName SysLogApiImpl
 * @Author salter <salter@vip.163.com>
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2022/4/25 11:08
 */
@Service
public class SysLogApiImpl implements SysLogApi {
    @Override
    public void putUpdate(JoinPoint joinPoint, String methodName, String name, String content) {
        try {
            // 各自的接口中需要定义好对应的方法才能调用
            Object[] params = joinPoint.getArgs();
            String classType = joinPoint.getTarget().getClass().getName();
            Class<?> clazz = Class.forName(classType);
            String clazzName = clazz.getName();
            Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName, params);
            if (!CollectionUtils.isEmpty(nameAndArgs)) {
                Iterator it = nameAndArgs.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next();
                    Object newObject = entry.getValue();
                    try {
                        Field id = newObject.getClass().getSuperclass().getDeclaredField("id");
                        id.setAccessible(true);
                        Object oldObject = EbeanUtil.initExpressionList(entry.getValue().getClass()).idEq(id.get(newObject)).findOne();
                        if (null != oldObject) {
                            String source = "";
                            String value = id.getAnnotation(ApiModelProperty.class).value();
                            if (StringUtils.isNotEmpty(value)) {
                                source += value + "：" + id.get(oldObject);
                            }
                            try {
                                Field fieldName = oldObject.getClass().getDeclaredField(name);
                                fieldName.setAccessible(true);
                                String nameValue = fieldName.getAnnotation(ApiModelProperty.class).value();
                                if (StringUtils.isNotEmpty(nameValue)) {
                                    source += " " + nameValue + "：" + fieldName.get(oldObject);
                                }
                            } catch (Exception ignored) {

                            }
                            content += "[" + source + "]" + " <br>";

                        }
                        content += BeanChangeUtil.contrastObj(oldObject, newObject) + "<br>";
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
            put(methodName, content);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void putSave(JoinPoint joinPoint, String methodName, String name, String value) {
        try {
            Object[] params = joinPoint.getArgs();
            String classType = joinPoint.getTarget().getClass().getName();
            Class<?> clazz = Class.forName(classType);
            String clazzName = clazz.getName();
            Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName, params);
            StringBuffer bf = new StringBuffer();
            if (!CollectionUtils.isEmpty(nameAndArgs)) {
                Iterator it = nameAndArgs.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry) it.next();
                    Object o1 = entry.getValue();
                    Field field = o1.getClass().getDeclaredField(name);
                    field.setAccessible(true);
                    String fieldText = field.getAnnotation(ApiModelProperty.class).value();
                    bf.append(":" + fieldText + "->" + field.get(o1));
                }
            }
            put(methodName, value + bf.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void putDelete(JoinPoint joinPoint, String methodName, String name, String value, String className) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            StringBuffer bf = new StringBuffer();
            String id = request.getParameter("id");
            Class<?> eClass = Class.forName(className);
            Object oObject = DB.find(eClass).where().idEq(id).findOne();
            Field field = oObject.getClass().getDeclaredField(name);
            field.setAccessible(true);
            String fieldText = field.getAnnotation(ApiModelProperty.class).value();
            bf.append(":" + fieldText + "->" + field.get(oObject));
            put(methodName, value + bf.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void put(String methodName, String content) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            SysLog log = new SysLog();
            String ip = IpUtils.getIPAddr(request);
            log.setIp(ip);
            log.setOperateType(methodName);
            log.setContent(content);
            log.save();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String, Object> map = new HashMap<String, Object>();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
            return map;
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            map.put(attr.variableName(i + pos), args[i]);//paramNames即参数名
        }
        return map;
    }


}
